using System;
using System.Threading;
using System.Windows.Forms;
using EternitySolver.Core;
using EternitySolver.Core.Solvers;
using EternitySolver.Core.Solvers.FixedPath;

namespace EternitySolver
{
    public partial class FormSolver : Form
    {
        private delegate void UpdateTimeTaken(long milliseconds);
        private delegate void UpdateCanvas(Board board);

        private Thread _solveThread;
        private int _placements;

        public FormSolver()
        {
            InitializeComponent();
        }

        private void FormSolver_Load(object sender, EventArgs e)
        {
            comboSolver.Items.Add(new SpiralSolver());
            comboSolver.Items.Add(new ForwardRowAndColumnSolver());
            comboSolver.Items.Add(new ReverseSpiralSolver());
            comboSolver.Items.Add(new RandomSolver());
            comboSolver.SelectedIndex = 0;
        }

        private void btnSolve_Click(object sender, EventArgs e)
        {
            btnSolve.Enabled = false;
            btnStop.Enabled = true;
            btnStop.Focus();
            milliseconds.Text = "-";

            labelPlacements.Text = "0 placements";
            _placements = 0;
            const int updateFrequency = 20;

            var board = Board.Create((int)numericSize.Value, (int)numericSize.Value, (int)numericColours.Value);
            var solver = (ISolver)comboSolver.Items[comboSolver.SelectedIndex];
            _solveThread = new Thread(t =>
                                         {
                                             solver.OnTilePlaced += (game =>
                                                                         {
                                                                             _placements++;
                                                                             labelPlacements.BeginInvoke(new MethodInvoker(UpdatePlacements));
                                                                             if (_placements % updateFrequency == 0)
                                                                             {
                                                                                 Invoke(new UpdateCanvas(UpdatePicture), game);
                                                                             }
                                                                             Application.DoEvents();
                                                                         });
                                             solver.OnSolved += ((game, timetaken) =>
                                                                     {
                                                                         Invoke(new UpdateCanvas(UpdatePicture), game);
                                                                         Invoke(new UpdateTimeTaken(UpdateTime), timetaken);
                                                                         BeginInvoke(new MethodInvoker(StopSolving));
                                                                     });
                                             solver.Solve(board);
                                         })
                              {
                                  IsBackground = true
                              };
            _solveThread.Start();
        }

        private void UpdateTime(long millis)
        {
            milliseconds.Text = string.Format("Took: {0}ms", millis);
        }

        private void UpdatePicture(Board board)
        {
            canvas.Image = board.GetBitmap(canvas.Width, canvas.Height);
        }

        private void UpdatePlacements()
        {
            labelPlacements.Text = _placements + " placements";
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            StopSolving();
        }

        private void StopSolving()
        {
            btnSolve.Enabled = true;
            btnStop.Enabled = false;
            btnSolve.Focus();
            if (_solveThread != null)
                _solveThread.Abort();
            _solveThread = null;
        }
    }
}